1 // Level Format: A basic API for 2D tile-based games 2 // Copyright (C) 2019 TheOnlyMrCat 3 // 4 // This program is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with this program. If not, see <https://www.gnu.org/licenses/>. 16 // 17 18 module mrcat.lvfmt.reader; 19 20 import std.array : appender; 21 import std.exception; 22 import std.stdio; 23 24 import mrcat.lvfmt.object; 25 import mrcat.lvfmt.level; 26 27 Level!T readLevel(T : BaseObject)(inout string filename) { 28 File file = File(filename); 29 auto l = new Level!T(); 30 31 const int width = file.rawRead(new int[1])[0]; 32 const int height = file.rawRead(new int[1])[0]; 33 const byte bitWidth = file.rawRead(new byte[1])[0]; 34 if (bitWidth % 8 != 0) throw new Exception("Invalid bit width"); 35 36 file.rawRead(new byte[3]); // Reserved header space 37 38 const byte bytes = bitWidth / 8; 39 40 l.map = new long[][height]; 41 42 for (int y = 0; y < height; y++) { 43 l.map[y] = new long[width]; 44 for (int x = 0; x < width; x++) { 45 byte[] buf = new byte[bytes]; 46 file.rawRead(buf); 47 48 l.map[y][x] = 0; 49 for (int i = 0; i < bytes; i++) { 50 l.map[y][x] += buf[i] << (bytes - i - 1) * 8; 51 } 52 } 53 } 54 55 auto objects = appender!(T[])(); 56 57 // File pointer operations to check for EOF 58 FILE* fp = file.getFP(); 59 int c = getc(fp); 60 61 while (!file.eof()) { 62 ungetc(c, fp); 63 T obj = new T; 64 obj.deserialize(file); 65 objects.put(obj); 66 c = getc(fp); 67 } 68 69 l.objects = objects[]; 70 71 return l; 72 } 73 74 version(unittest) { 75 class TestObject : BaseObject { 76 override void deserialize(File f) { 77 lVal = f.rawRead(new long[1])[0]; 78 } 79 80 override void serialize(File f) { 81 f.rawWrite([lVal]); 82 } 83 84 long lVal; 85 } 86 } 87 88 unittest { 89 auto l = readLevel!TestObject("test/map.lft"); 90 assert(l.map.length == 0x10); 91 assert(l.map[0].length == 0x10); 92 assert(l.map[4][2] == 0x05); 93 assert(l.objects[0].lVal == 0x10); 94 assert(l.objects[1].lVal == 0x1000); 95 }